package com.yudaob2b.elong.controller.domestic;

import com.alibaba.fastjson.JSONObject;
import com.yudaob2b.business.constant.RedisConstant;
import com.yudaob2b.business.entity.po.ElongHotelDetailPO;
import com.yudaob2b.business.entity.po.ElongHotelListPO;
import com.yudaob2b.business.entity.po.ElongHotelListTempPO;
import com.yudaob2b.business.service.ElongHotelDetailService;
import com.yudaob2b.business.service.ElongHotelListService;
import com.yudaob2b.business.service.ElongHotelListTempService;
import com.yudaob2b.business.service.RedisLockService;
import com.yudaob2b.common.utils.DateUtil;
import com.yudaob2b.common.utils.ExceptionUtil;
import com.yudaob2b.elong.entity.vo.hoteldetail.SingleHotel;
import com.yudaob2b.elong.entity.vo.hotellist.HotelIndex;
import com.yudaob2b.elong.entity.vo.hotellist.HotelXMLIndex;
import com.yudaob2b.elong.enums.OperationTypeEnum;
import com.yudaob2b.elong.service.HttpStaticService;
import com.yudaob2b.elong.task.QueryHotelDetailTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

/**
 * 酒店信息本地化（静态）
 *
 * @author Kidd.Tan
 * @email 298023780@qq.com
 * @date 7/5/18 10:18 PM
 */
@RestController
@RequestMapping("/api/local/hotelList")
public class ApiLocalHotelListController {
    private static Logger localHotelListLogger = LoggerFactory.getLogger("localHotelListLogger");

    // 每次更新数据库记录数
    private static int UPDATE_RECORD_LIMIT = 3000;
    // 线程最大运行数
    private int THREAD_MAX_COUNT = 10;

    @Resource(name = "httpStaticService")
    private HttpStaticService httpStaticService;

    @Resource(name = "elongHotelListTempService")
    private ElongHotelListTempService elongHotelListTempService;

    @Resource(name = "elongHotelListService")
    private ElongHotelListService elongHotelListService;

    @Resource(name = "elongHotelDetailService")
    private ElongHotelDetailService elongHotelDetailService;

    @Resource(name = "taskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Resource(name = "redisLockService")
    private RedisLockService redisLockService;

    @RequestMapping("/refresh")
    public String refresh() {
        long start = System.currentTimeMillis();
        String result = "ok";
        // redis业务锁
        try {
            if (redisLockService.lock(RedisConstant.REDIS_LOCK_KEY_LOCAL_HOTEL_LIST, 0)) {
                localHotelListLogger.info("启动艺龙静态酒店信息落地，开始时间：{} ", DateUtil.format(new Date(), DateUtil.PATTERN_CLASSICAL));
                // 删除临时表数据
                elongHotelListTempService.deleteAll();

                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                HotelIndex hotelIndex = httpStaticService.getHostList();
                List<ElongHotelListTempPO> tempPOList = new ArrayList<>();
                // 酒店列表保存到临时表
                for (HotelXMLIndex hotelXMLIndex : hotelIndex.getHotels()) {
                    try {
                        // 转换数据表实体对象
                        ElongHotelListTempPO elongHotelListTempPO = new ElongHotelListTempPO();
                        BeanUtils.copyProperties(hotelXMLIndex, elongHotelListTempPO);
                        elongHotelListTempPO.setHotelId(hotelXMLIndex.getHotelId());
                        elongHotelListTempPO.setStatus(Integer.valueOf(hotelXMLIndex.getStatus()));
                        elongHotelListTempPO.setUpdatedTime(sdf.parse(hotelXMLIndex.getUpdatedTime()));
                        tempPOList.add(elongHotelListTempPO);
                        if (tempPOList.size() >= UPDATE_RECORD_LIMIT) {
                            elongHotelListTempService.insertBatch(tempPOList);
                            tempPOList.clear();
                        }
                    } catch (ParseException e) {
                        localHotelListLogger.info("酒店列表保存临时表异常，hotelXMLIndex：{} ", JSONObject.toJSONString(hotelXMLIndex));
                        e.printStackTrace();
                    }
                }
                if (tempPOList.size() > 0) {
                    elongHotelListTempService.insertBatch(tempPOList);
                    tempPOList.clear();
                }

                //更新的酒店列表
                List<ElongHotelListTempPO> updateList = elongHotelListTempService.queryUpdateHotelList();
                encapsulation(updateList, OperationTypeEnum.UPDATE);

                //新增的酒店列表
                List<ElongHotelListTempPO> newList = elongHotelListTempService.queryNewlyHotelList();
                encapsulation(newList, OperationTypeEnum.ADD);
                localHotelListLogger.info("艺龙静态酒店信息落地完成，当前时间：{}，一共耗时：{} ", DateUtil.format(new Date(), DateUtil.PATTERN_CLASSICAL), (System.currentTimeMillis() - start));
            } else {
                localHotelListLogger.info("艺龙静态酒店信息落地线程运行中..............");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            redisLockService.unlock(RedisConstant.REDIS_LOCK_KEY_LOCAL_HOTEL_LIST);
        }
        localHotelListLogger.info("艺龙静态酒店信息落地结束");

        return result;
    }

    private void encapsulation(List<ElongHotelListTempPO> sourceList, OperationTypeEnum operationTypeEnum) {
        List<ElongHotelListPO> elongHotelListPOList = new ArrayList<>();
        List<ElongHotelDetailPO> elongHotelDetailPOList = new ArrayList<>();
        int limit = THREAD_MAX_COUNT;
        int listSize = sourceList.size();
        for (int i = 0; i < listSize; i += limit) {
            //作用为toIndex最后没有100条数据则剩余几条newList中就装几条
            if (i + limit > listSize) {
                limit = listSize - i;
            }
            List<ElongHotelListTempPO> processList = sourceList.subList(i, i + limit);
            List<FutureTask<ElongHotelDetailPO>> futureTaskList = new ArrayList<>(limit);
            // 多线程请求酒店详情
            for (ElongHotelListTempPO po : processList) {
                // 酒店列表
                ElongHotelListPO elongHotelListPO = getElongHotelListPO(po);
                elongHotelListPOList.add(elongHotelListPO);
                // 酒店详情
                QueryHotelDetailTask task = new QueryHotelDetailTask(String.valueOf(po.getHotelId()), httpStaticService);
                FutureTask<ElongHotelDetailPO> futureTask = new FutureTask<>(task);
                futureTaskList.add(futureTask);
                threadPoolTaskExecutor.execute(futureTask);
            }
            // 获取线程结果
            for (FutureTask<ElongHotelDetailPO> futureTask : futureTaskList) {
                try {
                    ElongHotelDetailPO elongHotelDetailPO = futureTask.get(60000, TimeUnit.MILLISECONDS);
                    if (elongHotelDetailPO != null) {
                        elongHotelDetailPOList.add(elongHotelDetailPO);
                    }
                } catch (Exception e) {
                    localHotelListLogger.info("多线程获取酒店详情异常，exception：{} ", ExceptionUtil.getTrace(e));
                    e.printStackTrace();
                }
            }

            // 达到UPDATE_RECORD_LIMIT量处理
            if (elongHotelListPOList.size() >= UPDATE_RECORD_LIMIT) {
                saveOrUpdateHotelList(elongHotelListPOList, operationTypeEnum);
            }
            // 达到UPDATE_RECORD_LIMIT量处理
            if (elongHotelDetailPOList.size() >= UPDATE_RECORD_LIMIT) {
                saveOrUpdateDetail(elongHotelDetailPOList, operationTypeEnum);
            }
        }
        // 处理未处理数据
        if (elongHotelListPOList.size() > 0) {
            saveOrUpdateHotelList(elongHotelListPOList, operationTypeEnum);
        }
        // 处理未处理数据
        if (elongHotelDetailPOList.size() > 0) {
            saveOrUpdateDetail(elongHotelDetailPOList, operationTypeEnum);
        }
    }

    /**
     * 获取酒店详情
     *
     * @param hotelId
     * @return
     */
    private ElongHotelDetailPO getElongHotelDetailPO(int hotelId) {
        ElongHotelDetailPO hotelDetailPO = null;
        SingleHotel singleHotel = httpStaticService.getHostDetail("cn", String.valueOf(hotelId));
        if (singleHotel != null) {
            hotelDetailPO = new ElongHotelDetailPO();
            hotelDetailPO.setHotel_id(singleHotel.getId());
            hotelDetailPO.setDetail(JSONObject.toJSONString(singleHotel.getDetail()));
            hotelDetailPO.setRoom(JSONObject.toJSONString(singleHotel.getRooms()));
            hotelDetailPO.setImage(JSONObject.toJSONString(singleHotel.getImages()));
            hotelDetailPO.setReview(JSONObject.toJSONString(singleHotel.getReview()));
        }

        return hotelDetailPO;
    }

    /**
     * 更新酒店信息
     *
     * @param list
     * @param operationTypeEnum
     */
    private void saveOrUpdateHotelList(List<ElongHotelListPO> list, OperationTypeEnum operationTypeEnum) {
        //localHotelListLogger.info("elongHotelList: {} , {}", operationTypeEnum, JSONObject.toJSONString(list));
        if (operationTypeEnum.equals(OperationTypeEnum.ADD)) {
            elongHotelListService.insertBatch(list);
        } else if (operationTypeEnum.equals(OperationTypeEnum.UPDATE)) {
            elongHotelListService.updateBatchById(list);
        }
        list.clear();
    }

    /**
     * 更新酒店详情
     *
     * @param list
     * @param operationTypeEnum
     */
    private void saveOrUpdateDetail(List<ElongHotelDetailPO> list, OperationTypeEnum operationTypeEnum) {
        //localHotelListLogger.info("elongHotelDetail: {} , {}", operationTypeEnum, JSONObject.toJSONString(list));
        if (operationTypeEnum.equals(OperationTypeEnum.ADD)) {
            elongHotelDetailService.insertBatch(list);
        } else if (operationTypeEnum.equals(OperationTypeEnum.UPDATE)) {
            elongHotelDetailService.updateBatchById(list);
        }
        list.clear();
    }

    /**
     * 通过elongHotelListTempPO对象转换ElongHotelListPO对象
     *
     * @param elongHotelListTempPO
     * @return
     */
    private ElongHotelListPO getElongHotelListPO(ElongHotelListTempPO elongHotelListTempPO) {
        ElongHotelListPO elongHotelListPO = new ElongHotelListPO();
        BeanUtils.copyProperties(elongHotelListTempPO, elongHotelListPO);
        return elongHotelListPO;
    }
}
